package gee

import (
	"fmt"
	"strings"
)

const (
	MODE_SPEICFIED = 1
	MODE_MATCH_ONE = 2
	MODE_MATCH_ALL = 3

	NAME_MATCH_ONE = ":"
	NAME_MATCH_ALL = "*"
)

type pathPart struct {
	raw         string // 原始节点内容
	key         string // key：固定匹配即 原始字符串； 匹配模式即 匹配标识
	name        string // 名称：固定匹配即 原始字符串； 匹配模式即 参数名
	patternMode int    // 匹配模式：1: 固定字符串匹配；2: :name 模式（一级）； 3: *name 模式（多级）
}

type pathNode struct {
	pathPart
	path     string               // 当前节点完整路径 part1/part2
	parent   *pathNode            // 父节点
	children map[string]*pathNode // 子节点
}

type nodeMatch struct {
	node   *pathNode         // 匹配节点
	params map[string]string // 模糊匹配项
}

func NewRootNode() *pathNode {

	return &pathNode{
		pathPart: pathPart{
			key:         "",
			raw:         "",
			name:        "",
			patternMode: MODE_SPEICFIED,
		},
		path:     "",
		parent:   nil,
		children: make(map[string]*pathNode),
	}
}

// 获取子节点
func (node *pathNode) getChild(part pathPart) (*pathNode, error) {

	childNode, ok := node.children[part.key]
	if !ok {
		return nil, nil
	}
	// check
	if childNode.raw != part.raw {
		return nil, fmt.Errorf("not matched child! %s vs %s", childNode.raw, part.raw)
	}

	return childNode, nil
}

// 添加一个新节点
func (node *pathNode) addChild(part pathPart) (*pathNode, error) {

	if node.patternMode == MODE_MATCH_ALL {
		return nil, fmt.Errorf("match-all node is not allowed to add child: %s", node.path)
	}

	check, err := node.getChild(part)
	if err != nil {
		return nil, fmt.Errorf("check child failed: %s", err.Error())
	}
	if check != nil {
		return check, nil
	}

	childNode := &pathNode{
		pathPart: part,
		path:     strings.Join([]string{node.path, part.raw}, "/"),
		parent:   node,
		children: make(map[string]*pathNode),
	}
	node.children[part.key] = childNode
	return childNode, nil
}

// 匹配子项
func (node *pathNode) matchChild(name string) []*nodeMatch {

	matches := []*nodeMatch{}

	// 精确匹配
	if check, ok := node.children[name]; ok {
		matches = append(matches, &nodeMatch{node: check, params: make(map[string]string)})
	}

	// 单项匹配
	if check2, ok := node.children[NAME_MATCH_ONE]; ok {
		matches = append(matches, &nodeMatch{node: check2, params: map[string]string{check2.name: name}})
	}

	// 模糊匹配
	if check3, ok := node.children[NAME_MATCH_ALL]; ok {
		matches = append(matches, &nodeMatch{node: check3, params: map[string]string{check3.name: name}})
	}

	return matches
}

func ParsePart(strPart string) pathPart {

	part := pathPart{
		key:         strPart,
		raw:         strPart,
		name:        strPart,
		patternMode: MODE_SPEICFIED,
	}
	if len(strPart) == 0 {
		return part
	}
	switch strPart[:1] {
	case NAME_MATCH_ONE:
		part.key = NAME_MATCH_ONE
		part.name = strPart[1:]
		part.patternMode = MODE_MATCH_ONE
	case NAME_MATCH_ALL:
		part.key = NAME_MATCH_ALL
		part.name = strPart[1:]
		part.patternMode = MODE_MATCH_ALL
	}
	return part
}

func Path2Parts(path string) []pathPart {

	if len(path) == 0 {
		return nil
	}
	if path[:1] == "/" {
		path = path[1:]
	}
	parts := []pathPart{}
	for _, v := range strings.Split(path, "/") {
		parts = append(parts, ParsePart(v))
	}
	return parts
}

// 外露方法： AddPath
func (node *pathNode) AddPath(path string) error {

	if len(path) == 0 || path[:1] != "/" {
		return fmt.Errorf("invalid path %s", path)
	}
	parts := Path2Parts(path)
	currentNode := node
	for i := 0; i < len(parts); i++ {
		part := parts[i]
		nextNode, err := currentNode.addChild(part)
		if err != nil {
			return fmt.Errorf("add path %s failed at part %s", path, part.raw)
		}
		currentNode = nextNode
	}

	return nil
}

// 外露方法： MatchByPath
// 会返回所有匹配的节点
func (node *pathNode) MatchByPath(path string) []*nodeMatch {

	if len(path) == 0 {
		return []*nodeMatch{}
	}
	if path[:1] == "/" {
		path = path[1:]
	}
	parts := strings.Split(path, "/")

	childMatches := node.matchChild(parts[0])
	// 已经是最终级，直接返回
	if len(parts) <= 1 {
		return childMatches
	}

	subPath := strings.Join(parts[1:], "/")
	matches := []*nodeMatch{}
	// 递归处理
	for i := 0; i < len(childMatches); i++ {
		childMatch := childMatches[i]
		// 子项为 * 匹配，直接结案
		if childMatch.node.patternMode == MODE_MATCH_ALL {
			childMatch.params[childMatch.node.name] = subPath
			matches = append(matches, childMatch)
			continue
		}
		subMatches := childMatch.node.MatchByPath(subPath)
		for j := 0; j < len(subMatches); j++ {
			// 拼接上 params
			subMatch := subMatches[j]
			for k, v := range childMatch.params {
				subMatch.params[k] = v
			}
			matches = append(matches, subMatch)
		}
	}
	return matches
}
